﻿using System.Collections.Generic;
using System.Text;
using UnityEngine;

public static class BundleManager
{
    public static AssetBundleManifest manifest;
    static List<BundleTask> m_Cache = new List<BundleTask>();

    /// <summary>
    /// 缓存的AssetBundle数目
    /// </summary>
    public static int cacheCount
    {
        get { return m_Cache.Count; }
    }

    /// <summary>
    /// 初始化
    /// </summary>
    public static void Init(string manifestName)
    {
        var bundle = LoadTask(manifestName).resource as AssetBundle;
        manifest = bundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
    }

    /// <summary>
    /// 更新入口
    /// </summary>
    public static void Update()
    {
        UpdateTaskes();
    }

    static BundleTask FindTaskImpl(string path, LoadSource source, IEnumerable<BundleTask> taskes)
    {
        foreach (var task in taskes)
        {
            if (task.PathEquals(path, source))
                return task;
        }
        return null;
    }

    /// <summary>
    /// 从缓存中查找任务
    /// </summary>
    public static BundleTask FindCache(string path, LoadSource source = LoadSource.Auto)
    {
        return FindTaskImpl(path, source, m_Cache);
    }

    static string[] GetDirectDependencies(string path, LoadSource source)
    {
        if (manifest != null)
            return manifest.GetDirectDependencies(path);
#if UNITY_EDITOR
        return GetDependenciesFromManifest(path, source);
#else
        return null;
#endif
    }

#if UNITY_EDITOR
    const string DEPENDENCIES_START = "Dependencies:";
    static readonly string DEPENDENCE_START = string.Format("- {0}/", LoadManager.packagePath);
    static string[] GetDependenciesFromManifest(string path, LoadSource source)
    {
        string manifestPath = path + ".manifest";
        var data = LoadManager.LoadData(manifestPath, source);
        if (data == null)
            return null;

        var text = Encoding.UTF8.GetString(data);
        var lines = text.Split('\n');
        int start = -1;
        for (int i = 0; i < lines.Length; ++i)
        {
            if (string.Equals(lines[i], DEPENDENCIES_START))
            {
                start = i;
                break;
            }
        }

        if (start < 0)
            return null;

        int index = DEPENDENCE_START.Length;
        var dependencies = new List<string>();
        for (int i = start + 1; i < lines.Length; ++i)
        {
            if (!lines[i].StartsWith(DEPENDENCE_START))
                break;

            dependencies.Add(lines[i].Substring(index));
        }

        return dependencies.ToArray();
    }
#endif

    #region 同步加载

    /// <summary>
    /// 同步加载AssetBundle及其依赖
    /// </summary>
    /// <param name="path">资源路径</param>
    /// <param name="source">资源来源</param>
    public static LoadTaskR LoadTask(string path, LoadSource source = LoadSource.Auto)
    {
        // 从缓存中查找
        var task = FindCache(path, source);
        if (task != null)
        {
            task.referenceCount++;
            return new LoadTaskR(task);
        }

        // 加载依赖
        var dependencies = GetDirectDependencies(path, source);
        LoadTaskR[] taskes = null;
        if (dependencies != null && dependencies.Length > 0)
        {
            taskes = new LoadTaskR[dependencies.Length];
            for (int i = 0; i < taskes.Length; ++i)
            {
                taskes[i] = LoadTask(dependencies[i], source);
                //if (taskes[i] == null)
                //{
                //    BundleTask.ReleaseDependencies(taskes);
                //    Debug.LogErrorFormat("load dependence {0} failed!", dependencies[i]);
                //    return null;
                //}
            }
        }

        // 加载AssetBundle
        task = FindLoadTask(path, source);
        if (task == null)
        {
            task = new BundleTask(null);
        }
        else
        {
            task.referenceCount++;
        }

        task.path = path;
        task.source = source;
        task.SetDone(LoadManager.LoadAssetBundle(path, source));
        task.SetDependencies(taskes);

        task.Update();
        m_Cache.Add(task);

        return new LoadTaskR(task);
    }
#endregion


#region 异步加载
    // 正在加载的任务
    static List<BundleTask> m_Taskes = new List<BundleTask>();

    /// <summary>
    /// 正在加载的任务数目
    /// </summary>
    public static int taskCount
    {
        get { return m_Taskes.Count; }
    }

    /// <summary>
    /// 异步加载AssetBundle，包括其依赖
    /// </summary>
    /// <param name="path">资源路径</param>
    /// <param name="onFinish">加载完成回调</param>
    /// <param name="source">资源来源</param>
    /// <param name="priority">资源加载优先级</param>
    /// <returns>返回资源加载任务对象，支持yield return，返回null表示资源或其依赖不存在</returns>
    public static LoadTaskR LoadAsync(string path, LoadSource source = LoadSource.Auto)
    {
        var task = FindCache(path, source);
        if (task != null)
        {
            var taskr = new LoadTaskR(task);
            task.referenceCount++;
            return taskr;
        }

        task = FindLoadTask(path, source);
        if (task != null)
        {
            task.referenceCount++;
            return new LoadTaskR(task);
        }

        // 加载依赖
        var dependencies = GetDirectDependencies(path, source);
        //Debug.Log(path + " " +dependencies.Length);

        LoadTaskR[] taskes = null;
        if (dependencies != null && dependencies.Length > 0)
        {
            taskes = new LoadTaskR[dependencies.Length];
            for (int i = 0; i < taskes.Length; ++i)
            {
                taskes[i] = LoadAsync(dependencies[i], source);
                //if (taskes[i] == null)
                //{
                //    BundleTask.ReleaseDependencies(taskes);
                //    Debug.LogErrorFormat("load dependence {0} failed!", dependencies[i]);
                //    return null;
                //}
            }
        }

        task = new BundleTask(LoadManager.LoadAssetBundleAsync(path, source));
        task.path = path;
        task.source = source;
        task.SetDependencies(taskes);
        m_Taskes.Add(task);
        return new LoadTaskR(task);
    }

    /// <summary>
    /// 查找加载中的任务
    /// </summary>
    public static BundleTask FindLoadTask(string path, LoadSource source = LoadSource.Auto)
    {
        return FindTaskImpl(path, source, m_Taskes);
    }

    // 更新正在加载的任务
    static void UpdateTaskes()
    {
        List<BundleTask> removes = new List<BundleTask>();

        for (int i = 0; i < m_Taskes.Count; ++i)
        {
            var task = m_Taskes[i];

            task.Update();
            if (task.isDone)
                removes.Add(task);
        }
        
        for (int i = 0; i < removes.Count; ++i)
        {
            var task = removes[i];
            m_Taskes.Remove(task);
            if (string.IsNullOrEmpty(task.error))
                m_Cache.Add(task);
        }
    }
    #endregion

    public static void UnloadAllAssetBundles(bool unloadAsset = false)
    {
        foreach(var item in m_Cache)
        {
            if(unloadAsset)
            {
                item.ReleaseBundleAssets();
            }
            else
            {
                item.Destroy();
            }
        }
        m_Cache.Clear();

        foreach (var item in m_Taskes)
        {
            item.Destroy();
        }
        m_Taskes.Clear();

    }

    public static void UnloadAssetBundles(string path, LoadSource source = LoadSource.Auto)
    {
        var task = FindCache(path, source);
        if (task == null)
        {
            return;
        }

        // 加载依赖
        var dependencies = GetDirectDependencies(path, source);
        //Debug.Log(path + " " +dependencies.Length);
        
        task.referenceCount--;
        if (task.referenceCount == 0)
        {
            task.ReleaseBundleAssets();
            for (int i = 0; i < m_Cache.Count; i++)
            {
                if(m_Cache[i] == task)
                {
                    m_Cache.RemoveAt(i);
                    break;
                }
            }

            if (dependencies != null && dependencies.Length > 0)
            {
                foreach (var dependency in dependencies)
                {
                    UnloadAssetBundles(dependency, source);
                }
            }
        }
    }
}
